#!/usr/bin/env python
# -*- coding: utf-8 -*-

import datetime

from farado.logger import logger
from farado.items.issue import Issue
from farado.items.issue_change import IssueChange
from farado.general_manager_holder import project_manager, raw_querier
from farado.helpers.long_action_watcher import LongActionHandler
from farado.helpers.issue_statistics_helper import states_from_issue_change



class IssueReportHelper(LongActionHandler):

    class UserIssuePeriod:
        def __init__(
                self,
                user_id=None,
                issue_id=None,
                state_id=None,
                start_date_time=None,
                ):
            self.user_id = user_id
            self.issue_id = issue_id
            self.state_id = state_id
            self.start_date_time = start_date_time
            self.end_date_time = None

        def from_value(self):
            return f'{self.start_date_time:%Y-%m-%d %H:%M:%S}'

        def to_value(self):
            return f'{self.end_date_time:%Y-%m-%d %H:%M:%S}'

    class UserIssuePeriodContainer:
        def __init__(self):
            # Основные данные
            self.container = []

            # Индексатор <issue_id, [index, index, index ...]>
            self.index = {}

            # Индексатор элементов на удаление
            self.remove_index = []

        def add_start(self, user_id, issue_id, state_id, value):
            # Финализация незакрытых периодов.
            if issue_id in self.index:
                for index in self.index[issue_id]:
                    item = self.container[index]
                    if not item.end_date_time:
                        item.end_date_time = value

            # Создание нового периода.
            self.container.append(
                IssueReportHelper.UserIssuePeriod(
                    user_id=user_id,
                    issue_id=issue_id,
                    state_id=state_id,
                    start_date_time=value,
                )
            )

            # Индексация итогового контейнера по issue_id.
            container_index = len(self.container) - 1
            if issue_id in self.index:
                self.index[issue_id].append(container_index)
            else:
                self.index[issue_id] = [container_index]

        def add_end(self, issue_id, value):
            # Финализация незакрытых периодов.
            if issue_id in self.index:
                for index in self.index[issue_id]:
                    item = self.container[index]
                    if not item.end_date_time:
                        item.end_date_time = value

        def truncate(self, from_value, to_value):
            # Усечение периодов завершенных до целевого
            self.container = [item
                for item in self.container
                if not item.end_date_time or item.end_date_time > from_value
            ]
            # замена начала периодов, начатых до from_date и добавление
            # незавершённых периодов to_date
            for item in self.container:
                if from_value > item.start_date_time:
                    item.start_date_time = from_value
                if not item.end_date_time:
                    item.end_date_time = to_value

        def filter_by_user(self, user_id):
            self.container = [
                item
                for item in self.container
                if str(item.user_id) == str(user_id)
            ]

        def container_by_user(self):
            # Формирует упорядоченный перечень пользователей
            result = {
                user.id:[]
                for user in project_manager().ordered_users()
                if user.id in {
                    item.user_id
                    for item in self.container
                    if item.user_id
                }
            }
            # Раскладывает данные по пользователям
            for item in self.container:
                result[item.user_id].append(item)
            return result

        def container_by_projects(self):
            # Формирует упорядоченный перечень проектов
            unique_issues_ids = {
                item.issue_id
                for item in self.container
                if item.issue_id
            }
            projects_issues = {}
            issues_projects = {}
            for issue_id in unique_issues_ids:
                if issue := project_manager().issue(issue_id):
                    issues_projects[issue_id] = issue.project_id
                    if issue.project_id in projects_issues:
                        projects_issues[issue.project_id].append(issue_id)
                    else:
                        projects_issues[issue.project_id] = [issue_id]
            result = {
                project_id:[]
                for project_id in sorted(
                    projects_issues.keys(),
                    key=lambda project_id: project_manager().project_caption(project_id)
                )
            }

            # Раскладывает данные по проектам
            for item in self.container:
                result[issues_projects[item.issue_id]].append(item)
            return result

        def issues_ids(self):
            return list({item.issue_id for item in self.container})

    def __init__(self):
        states = project_manager().states()
        self.active_states = [state.id for state in states if state.is_active()]

    #--------------------------------------------------------------------------#
    def active_issues_ids_in_period(self, from_date, to_date):
        from_date = datetime.datetime.strptime(from_date, '%d.%m.%Y')
        to_date = datetime.datetime.strptime(to_date, '%d.%m.%Y')

        ids = raw_querier().issue_changes_ids_with_state_before_date(to_date)
        issue_changes = project_manager().issue_changes_by_ids(ids)

        issues_states = {}
        for change in issue_changes:
            old_state_id, new_state_id = states_from_issue_change(change)
            if not old_state_id or not new_state_id:
                continue

            if new_state_id in self.active_states:
                issues_states[change.issue_id] = new_state_id
            elif change.date_time < from_date:
                if change.issue_id in issues_states:
                    del issues_states[change.issue_id]

        return list(issues_states.keys())

    #--------------------------------------------------------------------------#
    def active_issues_ids_in_period_by_user(
            self,
            from_date,
            to_date,
            user_id,
            ):
        from_date = datetime.datetime.strptime(from_date, '%d.%m.%Y')
        to_date = datetime.datetime.strptime(to_date, '%d.%m.%Y')
        to_date += datetime.timedelta(seconds=86399)

        ids = raw_querier().issue_changes_ids_with_state_before_date(
            to_date=to_date,
        )
        issue_changes = project_manager().issue_changes_by_ids(ids)

        result = self.UserIssuePeriodContainer()
        for change in issue_changes:
            current_user_id = change.user_id
            issue_id = change.issue_id
            date_time = change.date_time

            old_state_id, new_state_id = states_from_issue_change(change)
            if not old_state_id or not new_state_id:
                continue

            if new_state_id in self.active_states:
                result.add_start(current_user_id, issue_id, new_state_id, date_time)
            else:
                result.add_end(issue_id, date_time)

        result.truncate(from_date, to_date)
        result.filter_by_user(user_id)

        return result

    #--------------------------------------------------------------------------#
    def active_issues_ids_in_period_by_project(
            self,
            from_date,
            to_date,
            project_id,
            ):
        from_date = datetime.datetime.strptime(from_date, '%d.%m.%Y')
        to_date = datetime.datetime.strptime(to_date, '%d.%m.%Y')
        to_date += datetime.timedelta(seconds=86399)

        ids = raw_querier().issue_changes_ids_with_state_before_date_by_project(
            to_date=to_date,
            project_id=project_id,
        )
        issue_changes = project_manager().issue_changes_by_ids(ids)

        result = self.UserIssuePeriodContainer()
        for change in issue_changes:
            user_id = change.user_id
            issue_id = change.issue_id
            date_time = change.date_time

            old_state_id, new_state_id = states_from_issue_change(change)
            if not old_state_id or not new_state_id:
                continue

            if new_state_id in self.active_states:
                result.add_start(user_id, issue_id, new_state_id, date_time)
            else:
                result.add_end(issue_id, date_time)

        result.truncate(from_date, to_date)
        return result
